#include "Texture.h"

#define uchar unsigned char
Texture::Texture(const char* fileName, int keyR, int keyG, int keyB)
{
	BMPClass bmp;
	BMPClass::BMPLoad(fileName, bmp);
	Width = bmp.width;
	Height = bmp.height;

	BYTE* Filtered = new BYTE[4 * Width * Height];
	for (int y=0; y<Height; ++y)
	{
		for (int x=0; x<Width; ++x)
		{
			BYTE* Old = &bmp.bytes[y*Width*3 + x*3];
			BYTE* New = &Filtered[y*Width*4 + x*4];

			for (int i=0; i<3; ++i) New[i] = Old[i];
			if (New[0] == keyR &&
				New[1] == keyG &&
				New[2] == keyB) New[3] = 0;
			else New[3] = 255;
		}
	}

	glGenTextures(1, &TexId);
    glBindTexture(GL_TEXTURE_2D, TexId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Filtered);

	delete[] Filtered;
}
Texture::Texture(const char* fileName, const char* rawFile)
{
	BMPClass bmp;
	BMPClass::BMPLoad(fileName, bmp);
	Width = bmp.width;
	Height = bmp.height;

	BYTE* Filtered = new BYTE[4 * Width * Height];
	for (int y=0; y<Height; ++y)
	{
		for (int x=0; x<Width; ++x)
		{
			BYTE* Old = &bmp.bytes[y*Width*3 + x*3];
			BYTE* New = &Filtered[y*Width*4 + x*4];

			for (int i=0; i<3; ++i) New[i] = Old[i];
			if (New[0] == 255 &&
				New[1] == 0 &&
				New[2] == 255) New[3] = 0;
			else New[3] = 255;
		}
	}

	std::ofstream raw(rawFile, std::ios::binary);
	for (int i=0; i<Width*Height; ++i) raw.write((char*)&Filtered[i*4], 1);
	raw.close();

	glGenTextures(1, &TexId);
    glBindTexture(GL_TEXTURE_2D, TexId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Filtered);

	delete[] Filtered;
}
void Texture::Destroy()
{
    glDeleteTextures(1, &TexId);
}
Texture::~Texture()
{
}
GLuint Texture::operator()()
{
	return TexId;
}

BMPClass::BMPClass()
{
	bytes=0;
}
BMPClass::~BMPClass()
{
	delete[] bytes;
}
BYTE& BMPClass::pixel(int x,int y,int c)
{
	return bytes[(y*width+x)*3+c];
}
void BMPClass::allocateMem()
{
	delete[] bytes;
	bytes=new BYTE[width*height*3];
}
std::string BMPClass::TranslateBMPError(BMPError err)
{
	switch(err)
	{
	case(BMPNOTABITMAP):
		return "This file is not a bitmap, specifically it doesn't start 'BM'";
	case(BMPNOOPEN):
		return "Failed to open the file, suspect it doesn't exist";
	case(BMPFILEERROR):
		return "ferror said we had an error. This error seems to not always mean anything, try ignoring it";
	case(BMPBADINT):
		return "sizeof(int)!=4 quite a lot of rewriting probably needs to be done on the code";
	case(BMPNOERROR):
		return "No errors detected";
	case(BMPUNKNOWNFORMAT):
		return "Unknown bmp format, ie not 24bit, 256,16 or 2 colour";
	default:
		return "Not a valid error code";
	}
}
BMPError BMPClass::BMPLoad(std::string fname,BMPClass& bmp)
{
	if(sizeof(int)!=4) return BMPBADINT;

	FILE* f=fopen(fname.c_str(),"rb");		//open for reading in binary mode
	if(!f) return BMPNOOPEN;
	char header[54];
	fread(header,54,1,f);			//read the 54bit main header

	if(header[0]!='B' || header[1]!='M')
	{
		fclose(f);
		return BMPNOTABITMAP;		//all bitmaps should start "BM"
	}

	//it seems gimp sometimes makes its headers small, so we have to do this. hence all the fseeks
	int offset=*(unsigned int*)(header+10);

	bmp.width=*(int*)(header+18);
	bmp.height=*(int*)(header+22);
	//now the bitmap knows how big it is it can allocate its memory
	bmp.allocateMem();

	int bits=int(header[28]);		//colourdepth

	int x,y,c;
	BYTE cols[256*4];				//colourtable
	switch(bits)
	{
	case(24):
		fseek(f,offset,SEEK_SET);
		for(y=bmp.height-1; y>=0; --y) fread(bmp.bytes + y*bmp.width*3, bmp.width*3, 1, f);	//24bit is easy
		for(x=0;x<bmp.width*bmp.height*3;x+=3)			//except the format is BGR, grr
		{
			BYTE temp=bmp.bytes[x];
			bmp.bytes[x]=bmp.bytes[x+2];
			bmp.bytes[x+2]=temp;
		}
		break;

	case(8):
		fread(cols,256*4,1,f);							//read colortable
		fseek(f,offset,SEEK_SET);
		for(y=bmp.height-1;y>=0;--y)						//(Notice 4bytes/col for some reason)
			for(x=0;x<bmp.width;++x)
			{
				BYTE byte;
				fread(&byte,1,1,f);						//just read byte
				for(int c=0;c<3;++c)
					bmp.pixel(x,y,c)=cols[byte*4+2-c];	//and look up in the table
			}
		break;

	case(4):
		fread(cols,16*4,1,f);
		fseek(f,offset,SEEK_SET);
		for(y=0;y<256;++y)
			for(x=0;x<256;x+=2)
			{
				BYTE byte;
				fread(&byte,1,1,f);						//as above, but need to exract two
				for(c=0;c<3;++c)						//pixels from each byte
					bmp.pixel(x,y,c)=cols[byte/16*4+2-c];
				for(c=0;c<3;++c)
					bmp.pixel(x+1,y,c)=cols[byte%16*4+2-c];
			}
		break;

	case(1):
		fread(cols,8,1,f);
		fseek(f,offset,SEEK_SET);
		for(y=0;y<bmp.height;++y)
			for(x=0;x<bmp.width;x+=8)
			{
				BYTE byte;
				fread(&byte,1,1,f);
				//Every byte is eight pixels
				//so I'm shifting the byte to the relevant position, then masking out
				//all but the lowest bit in order to get the index into the colourtable.
				for(int x2=0;x2<8;++x2)
					for(int c=0;c<3;++c)
						bmp.pixel(x+x2,y,c)=cols[((byte>>(7-x2))&1)*4+2-c];
			}
		break;

	default:
		fclose(f);
		return BMPUNKNOWNFORMAT;
	}

	if(ferror(f))
	{
		fclose(f);
		return BMPFILEERROR;
	}

	fclose(f);

	return BMPNOERROR;
}
